﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NLog;
using Stee.CAP8.MessageBus.Interface.Interfaces;
using Stee.CAP8.MessageBus.Interface.Models;
using Stee.CAP8.MessageBus.Sagas.Core.Extensions;
using Stee.CAP8.MessageBus.Sagas.Core.Messages;
using Stee.CAP8.MessageBus.Sagas.Core.Sagas;
using System;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;

namespace Stee.CAP8.MessageBus.Sagas.Core
{
    public class EventBus : IBus
    {

        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        #region Topic Information



        private readonly IConfigurationRoot ConfigurationRoot;
        private readonly string topic = "";
        public const string ConfigurationFileJSON = "appsettings.json";


        private readonly TopicName topicNameObject = null;

        #endregion Topic Information

        #region Publisher & Subscriber

        private ISubscriber _objectSubscriber;

        private IPublisher _objectPublisher;

        #endregion Publisher & Subscriber

        #region Service Provider & Others

        private readonly IServiceProvider _provider;

        private readonly IHandleMessage<CommandMessage> _messageHandler;

        #endregion Service Provider & Others




        public EventBus(IServiceProvider provider,
            ISubscriber objectSubscriber,
            IPublisher objectPublisher,
            IHandleMessage<CommandMessage> messageHandler
            )
        {
            _provider = provider;
            _objectPublisher = objectPublisher;
            _objectSubscriber = objectSubscriber;
            ConfigurationRoot = new ConfigurationBuilder()
                .AddJsonFile(ConfigurationFileJSON, false)
                .Build();
            topic = ConfigurationRoot.GetSection("MessageBusPublish").GetSection("TopicID").Value;
            topicNameObject = new TopicName(topic, topic);
            _messageHandler = messageHandler;
        }




        public void HandleEvent()
        {
            //topic read co
            _objectSubscriber.Initialize(topicNameObject, OnCommandReceived, OnSubscriberError);

            if (_objectSubscriber.IsInitialized)
            {
                _objectSubscriber.Subscribe();
            }
        }

        public void SendCommand(CommandMessage message)
        {
            TopicName topicInfo = null;

            topicInfo = new TopicName(message.Source, message.Source);

            this._objectPublisher = _provider.GetService<IPublisher>();
            this._objectPublisher.Initialize(this.OnComandSended, OnPublisherError);
            if (_objectPublisher.IsInitialized)
            {
                var objectPublishToken = new CancellationTokenSource();
                if (this._objectPublisher.PublishCommand(topicInfo, message, objectPublishToken.Token))
                {
                    string msg = $"Publish CommandMessage object => {message}";
                    Console.WriteLine(msg);
                }
                else
                {
                    string msg = $"[Error] Publish CommandMessage object failed => {message}";
                    Console.WriteLine(msg);
                }
            }

        }

        #region OnSubscribe
        /// <summary>
        /// Mock example method name format, "On<Action><Response>".
        /// Error handler for subscriber.
        /// </summary>
        /// <param name="errorCode">Broker error code</param>
        /// <param name="isFatal">Flag indicating fatal error</param>
        private static void OnSubscriberError(string errorCode, bool isFatal)
        {
            if (string.IsNullOrEmpty(errorCode))
            {
                Logger.Warn($"Unexpected subscriber error, no error code returned. Is fatal: {isFatal}");
            }
            else
            {
                Logger.Error($"Subscriber error: {errorCode}, Is fatal: {isFatal}");
            }
        }

        /// <summary>
        /// Handle message of CommandMessage type
        /// </summary>
        /// <param name="topicName">Topic name</param>
        /// <param name="message">Command message</param>
        /// <returns>Return TRUE if consumed successfully else FALSE</returns>
        private bool OnCommandReceived(TopicName topicName, CommandMessage message)
        {

            try
            {
                #region Check & Log
                if (topicName == null)
                {
                    throw new ArgumentNullException(nameof(topicName));
                }
                else if (message == null)
                {
                    throw new ArgumentNullException(nameof(message));
                }

                //// Consume command message type
                string output = $"Consumed command message <CommandMessage>: {message}";

                Console.WriteLine(output);
                Logger.Info("Message consumed successfully.");
                #endregion

                //利用依赖倒置调用Client定义的Hanlder来执行Client当中对message的处理
                _messageHandler.Handle(message);
                return true;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, ex.Message);
                throw;
            }
        } 
        #endregion

        #region OnSended
        /// <summary>
        /// Mock example method name format, "On<Type><Action>".
        /// Publish response handler for Person data type messages.
        /// </summary>
        /// <param name="publishOutcome">Message publish outcome</param>
        /// <returns>Return TRUE if handled successfully else FALSE</returns>
        private bool OnComandSended(PublishOutcome publishOutcome)
        {
            bool isHandled = false;

            if (publishOutcome != null)
            {
                //// Handle your process outcome here
                isHandled = true;
            }

            return isHandled;
        }

        /// <summary>
        /// Mock example method name format, "On<Action><Response>".
        /// Publish error handler for broker publish error.
        /// </summary>
        /// <param name="errorCode">Publish error code</param>
        /// <param name="isFatal">Flag indicating fatal error</param>
        private void OnPublisherError(string errorCode, bool isFatal)
        {
            if (string.IsNullOrEmpty(errorCode))
            {
                Logger.Warn($"Unexpected publisher error, no error code returned. Is fatal: {isFatal}");
            }
            else
            {
                Logger.Error($"Publisher error: {errorCode}, Is fatal: {isFatal}");
            }
        } 
        #endregion




    }





}
